#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>

#include <cm/character/lattice.h>
#include <cm/character/character.h>
#include <cm/file/cm_file.h>

static char *hzk_data = NULL;
static char *asc_data = NULL;

/*
 * mask用来检查一个字节的各个bit位是否被置位
 * 如 ch & LATTICE_MASK[0] != 0 表示 ch的最高位是1
 */
static unsigned char LATTICE_MASK[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04,
		0x02, 0x01 };

/*
 * @brief	该函数计算ch所对应的点阵数据，在汉字库hzk16中的偏移
 * @return	成功则返回点阵数据在hzk16中的偏移,失败返回-1
 */
static inline int hzk16_offset(const struct gb2312_char *ch)
{
	if (ch->area <= GB2312_AREA_BASE
			|| ch->area > (GB2312_AREA_BASE + GB2312_AREA_TOTAL)) {
		printf("wrong area(%.2x).\n", ch->area);
		return -1;
	}
	if (ch->pos <= GB2312_POS_BASE
			|| ch->pos > GB2312_POS_BASE + GB2312_POS_PER_AREA) {
		printf("wrong position(%.2x).\n", ch->pos);
		return -1;
	}
	return (((GB2312_POS_PER_AREA * (ch->area - GB2312_AREA_BASE - 1))
			+ (ch->pos - GB2312_POS_BASE - 1)) * HZK16_BYTE_PER_CHAR);
}

static inline int asc16_offset(unsigned char asc)
{
	return (asc * ASC16_BYTE_PER_CHAR);
}

/*
 * @brief	从文件file中读取ASC码点阵数据,保存在全局指针asc_data中
 * @return	读取成功返回0，失败返回-1
 */
static int asc_data_init(const char *file)
{
	static const size_t ASC_SIZE = (128 * ASC16_BYTE_PER_CHAR);
	if (asc_data == NULL)
		asc_data = calloc(1, ASC_SIZE);
	if (asc_data == NULL)
		return -1;

	size_t s = cm_fread(file, asc_data, ASC_SIZE);
	if (s < ASC_SIZE)
		return -1;
	else
		return 0;
}

/*
 * @brief	从文件file中读取汉字库数据,保存在全局指针hzk_data中
 * @return	读取成功返回0，失败返回-1
 */
static int hzk_data_init(const char *file)
{
	/* 以二级汉字的最大区域确定文件大小 */
	static const size_t HZK_SIZE = (GB2312_AREA_LEVEL2_MAX
			* GB2312_POS_PER_AREA * HZK16_BYTE_PER_CHAR);

	if (hzk_data == NULL)
		hzk_data = calloc(1, HZK_SIZE);

	if (hzk_data == NULL)
		return -1;

	size_t s = cm_fread(file, hzk_data, HZK_SIZE);
	if (s < HZK_SIZE)
		return -1;
	else
		return 0;
}

/*
 * @brief	初始化汉字及ASC码的点阵数据,在调用offset和get等函数时必须先调用该函数
 * @param	hzk16 是汉字库的文件路径, asc16 是ASC字符库的文件路径
 * @return	成功返回0， 失败返回-1
 */
int lattice16_init(const char *hzk16, const char *asc16)
{
	if (hzk16 == NULL || asc16 == NULL)
		return -1;
	if (0 != hzk_data_init(hzk16)) {
		printf("init hzk failed.\n");
		return -1;
	}
	if (0 != asc_data_init(asc16)) {
		printf("init asc failed.\n");
		return -1;
	}

	return 0;

}

/*
 * @brief	获取汉字在点阵字库中对应的点阵数据
 * @param	ch: 是一个以GB2312编码的两字节汉字
 * @param	lattice: 作为出参，用来存放点阵数据
 * @return	获取成功则返回0，且点阵数据存放在lattice中，失败返回-1，lattice中内容不确定
 */
int hzk16_get(const struct gb2312_char *ch, char lattice[HZK16_BYTE_PER_CHAR])
{
	if (hzk_data == NULL)
		return -1;

	int offset = hzk16_offset(ch);
	if (offset < 0)
		return -1;
	else
		memcpy(lattice, &hzk_data[offset], HZK16_BYTE_PER_CHAR);
	return 0;
}

/*
 * @brief	获取ASC字符ch在点阵字库中对应的点阵数据
 * @param	lattice: 作为出参，用来存放点阵数据
 * @return	获取成功则返回0，且点阵数据存放在lattice中，失败返回-1，lattice中内容不确定
 */
int asc16_get(unsigned char ch, char lattice[16])
{
	if (asc_data == NULL)
		return -1;

	int offset = asc16_offset(ch);
	if (offset < 0)
		return -1;
	else
		memcpy(lattice, &asc_data[offset], ASC16_BYTE_PER_CHAR);
	return 0;
}

/*
 * @brief	对点阵中每一个被设置的点,调用回调函数cb
 * @param	lattice32: 是表示某个汉字点阵的32字节数据, arg是要传给cb的参数
 */
void hzk_foreach(const char *lattice32, lattice_callback cb, void *arg)
{
	int row, byte, bit, flag;
	for (row = 0; row < HZK16_ROW; row++) {
		for (byte = 0; byte < HZK16_BYTE_PER_ROW; byte++) {
			char temp = lattice32[row * HZK16_BYTE_PER_ROW + byte];
			for (bit = 0; bit < 8; bit++) {
				flag = temp & LATTICE_MASK[bit];
				if (flag)
					cb(row, byte * 8 + bit, 1, arg);
				else
					cb(row, byte * 8 + bit, 0, arg);

			}
		}
	}
}

/*
 * @brief	对点阵中每一个被设置的点,调用回调函数cb
 * @param	lattice16: 是表示某个ASC字符点阵的32字节数据, arg是要传给cb的参数
 */
void asc_foreach(const char *lattice16, lattice_callback cb, void *arg)
{
	int row, bit, flag;
	for (row = 0; row < ASC16_ROW; row++) {
		for (bit = 0; bit < 8; bit++) {
			flag = lattice16[row] & LATTICE_MASK[bit];
			if (flag)
				cb(row, bit, 1, arg);
			else
				cb(row, bit, 0, arg);
		}
	}
}

// demo of lattice
/*
 int main(int argc, char **argv)
 {
 if (0 != lattice16_init("./hzk16", "./ASC16")) {
 return 0;
 }

 if (argc >= 2) {
 char utf8[256] = { 0 };
 strncpy(utf8, argv[1], 256);
 char ch[256] = { 0 };
 size_t s = sizeof(utf8);
 size_t d = sizeof(ch);
 char *src = utf8;
 char *dst = ch;
 UTF82GB2312(&src, &s, &dst, &d);

 gb2312_print((unsigned char*) ch);
 }
 return 0;
 }
 */
